/**
 * Copyright 2011 Joe Choi, All rights reserved.
 */

package tao.calendar.gregorian;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import tao.calendar.gregorian.GregorianDate.Era;
import tao.time.AbstractTime;

/**
 * The Concrete GregorianDateTime class is a concrete implementation for the GregorianDateTime interface
 * 
 * @author Joe Choi
 * @version 1.0.0
 */
class ConcreteGregorianDateTime extends AbstractTime implements GregorianDateTime {
	/**
	 * Create a GregorianDateTime object by specifying the year, month, day, hour, minute and second.
	 * The millisecond is set to zero.
	 */
	public ConcreteGregorianDateTime(int year, int month, int day, int hour, int min, int sec) {
		this(year, month, day, hour, min, sec, 0);
	}

	/**
	 * Create a GregorianDateTime object from the value of a GregorianTime object.
	 * The date is set to epoch. That is January 1 1970
	 */
	public ConcreteGregorianDateTime(GregorianTime time) {
		this(new ConcreteGregorianDate(0), time);
	}
	
	/**
	 * Create a GregorianDateTime object from the value of a GregorianDate object.
	 * The time is set to 0:0:0.0
	 */
	public ConcreteGregorianDateTime(GregorianDate date) {
		this(date, new ConcreteGregorianTime(0));
	}

	/**
	 * Create a GregorianDateTime object from the value of a GregorianDate object.
	 * The time is set to 0:0:0.0
	 */
	public ConcreteGregorianDateTime(GregorianDateTime dt) {
		this(dt.scalar());
	}	
	
	/**
	 * Create a GregorianDateTime object from scalar
	 */
	public ConcreteGregorianDateTime(long time) {
		set(time);
	}

	/**
	 * Create a GregorianDateTime object by specifying the year, month, day, 
	 * hour, minute, second and millisecond 
	 */
	public ConcreteGregorianDateTime(int year, int month, int day, int hour, int min, int sec, int ms) {
		set(year, month, day, hour, min, sec, ms);
	}
		
	/**
	 * Create a GregorianDateTime object by specifying the era, year, month, day, 
	 * hour, minute, second and millisecond 
	 */
	public ConcreteGregorianDateTime(Era era, int year, int month, int day, int hour, int min, int sec, int ms) {
		set(era, year, month, day, hour, min, sec, ms);
	}	
	
	/**
	 * Create a GregorianDateTime object from the value of a GregorianDate object and a GregorianTime object
	 */
	public ConcreteGregorianDateTime(GregorianDate date, GregorianTime time) {
		set(date.getEra(), date.getYear(), date.getMonth(), date.getDay(), 
			   time.getHour(), time.getMinute(), time.getSecond(), time.getMillisecond());
	}

	@Override
	public long scalar() {
		return(date.scalar() + time.scalar());
	}
	
	@Override
	public String toString() {
		return(date.toString() + " " + time.toString());
	}
		
	@Override
	public Era getEra() {
		return(date.getEra());
	}

	@Override
	public int getYear() {
		return(date.getYear());
	}

	@Override
	public int getMonth() {
		return(date.getMonth());
	}
	
	@Override
	public int getDay() {
		return(date.getDay());
	}

	@Override
	public int getHour() {
		return(time.getHour());
	}

	@Override
	public int getMinute() {
		return(time.getMinute());
	}

	@Override
	public int getSecond() {
		return(time.getSecond());
	}
	
	public int getMillisecond() {
		return(time.getMillisecond());
	}

	protected void setInternalEra(Era era) {
		date.setEra(era);
	}

	protected void setInternalYear(int year) {
		date.setYear(year);
	}

	protected void setInternalMonth(int month) {
		date.setMonth(month);
	}
	
	protected void setInternalDay(int day) {
		date.setDay(day);
	}

	protected void setInternalHour(int hour) {
		time.setHour(hour);
	}

	protected void setInternalMinute(int minute) {
		time.setMinute(minute);
	}

	protected void setInternalSecond(int second) {
		time.setSecond(second);
	}

	protected void setInternalMillisecond(int millisecond) {
		time.setMillisecond(millisecond);
	}

	protected void set(GregorianDate date, GregorianTime time) {
		set(date.getEra(), date.getYear(), date.getMonth(), date.getDay(), 
				   time.getHour(), time.getMinute(), time.getSecond(), time.getMillisecond());	
	}
	
	protected void set(Calendar c) {
		set(c.get(Calendar.ERA) == GregorianCalendar.AD ? Era.AD : Era.BC,
				c.get(Calendar.YEAR), c.get(Calendar.MONTH) + 1, c.get(Calendar.DAY_OF_MONTH),
				c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), c.get(Calendar.SECOND), 
				c.get(Calendar.MILLISECOND)
		);		
	}

	private void set(long scalar) {
		Calendar c = getCalendar();
		c.setTimeInMillis(scalar);
		set(c);
	}
	
	protected Calendar getCalendar() {
		return(new GregorianCalendar(TimeZone.getTimeZone("GMT+00:00")));
	}
	
	private void set(int year, int month, int day, int hour, int min, int sec, int ms) {
		date = new ConcreteModifiableDate(new ConcreteGregorianDate(year, month, day));
		time = new ConcreteModifiableTime(new ConcreteGregorianTime(hour, min, sec, ms));
	}	
	
	private void set(Era era, int year, int month, int day, int hour, int min, int sec, int ms) {
		date = new ConcreteModifiableDate(new ConcreteGregorianDate(era, year, month, day));
		time = new ConcreteModifiableTime(new ConcreteGregorianTime(hour, min, sec, ms));
	}
	
	private ConcreteModifiableDate date;
	private ConcreteModifiableTime time;	
}